%% Author: vivimice
%% Created: Feb 16, 2012
%% Description: TODO: Add description to select_field
-module(select_field).

%%
%% Include files
%%
-include("keys.hrl").
-include("tchar.hrl").
-include("tstring.hrl").

%%
%% Records
%%
-record(config, {options, default_index}).
-record(cache, {total_options, option_widths, total_width}).

%%
%% Exported Functions
%%
-export([create_option/2, create_config/1, create_config/2, get_choice/2]).

%%
%% API Functions
%%

% label is tstring, Data can be any term
create_option(Label, Data) ->
	{Label, Data}.

create_config([FirstOption|RestOptions]) ->
	create_config([FirstOption|RestOptions], 0);
create_config(_) ->
	erlang:error(badarg).

create_config(Options, DefaultOptionIndex)
  when erlang:is_list(Options), 
	   erlang:length(Options) > 0, 
	   DefaultOptionIndex >= 0,
	   DefaultOptionIndex < erlang:length(Options) ->
	#config{options=Options, default_index=DefaultOptionIndex}.

get_choice(Context, Config) ->
	Options = Config#config.options,
	DefaultIndex = Config#config.default_index,
	{Widths, TotalWidth} = calculate_option_widths(Options, [], 0),
	
	Cache = #cache{total_options = erlang:length(Options),
				   option_widths = Widths,
				   total_width = TotalWidth},
	tio_manager:write(Context, <<$[>>),
	draw(Context, Options, DefaultIndex, Cache),
	loop(Context, Options, DefaultIndex, Cache).

%%
%% Local Functions
%%
loop(Context, Options, SelectedIndex, Cache) ->
	case tio_manager:read(Context) of
		
		{control, ?KEY_LEFT} when SelectedIndex > 0 ->
			NewSelectedIndex = SelectedIndex - 1,
			redraw(Context, Options, NewSelectedIndex, Cache),
			loop(Context, Options, NewSelectedIndex, Cache);
		
		{control, ?KEY_TAB} ->
			ok;
		
		{control, ?KEY_RIGHT} when SelectedIndex < Cache#cache.total_options - 1 ->
			NewSelectedIndex = SelectedIndex + 1,
			redraw(Context, Options, NewSelectedIndex, Cache),
			loop(Context, Options, NewSelectedIndex, Cache);
		
		{control, ?KEY_HOME} when SelectedIndex > 0 ->
			NewSelectedIndex = 0,
			redraw(Context, Options, NewSelectedIndex, Cache),
			loop(Context, Options, NewSelectedIndex, Cache);
		
		{control, ?KEY_END} when SelectedIndex < Cache#cache.total_options - 1 ->
			NewSelectedIndex = Cache#cache.total_options - 1,
			redraw(Context, Options, NewSelectedIndex, Cache),
			loop(Context, Options, NewSelectedIndex, Cache);
		
		{control, ?KEY_ENTER} ->
			get_option_data(Options, SelectedIndex);
		
		{tchar, TChar} ->
			IsSpace = tchar:equals(TChar, ?TC_SPACE),
			if
				IsSpace ->
					get_option_data(Options, SelectedIndex);
				true ->
					loop(Context, Options, SelectedIndex, Cache)
			end;

		_ ->
			loop(Context, Options, SelectedIndex, Cache)
	end.

redraw(Context, Options, SelectedIndex, Cache) ->
	tio_manager:move_cursor_horizontally(Context, -(Cache#cache.total_width + 1)),
	draw(Context, Options, SelectedIndex, Cache).

draw(Context, [], _SelectedIndex, _Cache) ->
	tio_manager:write(Context, <<$]>>),
	tio_manager:flush(Context);
draw(Context, [Option|Rest], SelectedIndex, Cache) ->
	TotalOptions = Cache#cache.total_options,
	CurrentIndex = TotalOptions - erlang:length(Rest) - 1,
	if
		CurrentIndex == SelectedIndex ->
			tio_manager:write(Context, <<"\x1b[1;33;44m">>),
			tio_manager:write(Context, get_label(Option)),
			tio_manager:write(Context, <<"\x1b[0m">>);
			
		true ->
			tio_manager:write(Context, get_label(Option))
	end,
	
	if
		Rest /= [] ->
			tio_manager:write(Context, tchar:new_ansi($|));
		true ->
			ok
	end,
	
	draw(Context, Rest, SelectedIndex, Cache).

calculate_option_widths([], Widths, TotalWidth) ->
	{Widths, TotalWidth};
calculate_option_widths([Option|Rest], Widths, TotalWidth) ->
	OptionWidth = ?TSTRING_WIDTH(get_label(Option)),
	if 
		% first item
		TotalWidth == 0 ->
			calculate_option_widths(Rest, Widths ++ [OptionWidth], OptionWidth);
		
		% not first item, take consideration of spacing char
		true ->
			calculate_option_widths(Rest, Widths ++ [OptionWidth], TotalWidth + OptionWidth + 1)
	end.

get_label(Option) ->
	{Label, _} = Option,
	Label.

get_option_data([Option|_], 0) ->
	{_, Data} = Option,
	Data;
get_option_data([_|Rest], Index) ->
	get_option_data(Rest, Index - 1);
get_option_data([], _) ->
	erlang:error(index_out_of_bounds).
